#include <avr/io.h>
#include <util/delay.h>
#include <avr/cpufunc.h>
#include <string.h>

#include "hardware.h"
#include "iolgt8f0xa.h"
#include "ircode.h"
#include "gpio.h"


#define DELAY_DIV	0.61
#define CA(US)		(US / DELAY_DIV)

// low time  = 1.06us + (1us * n)
// high time = 1.38us + (1us * n)
static ir_code_t IR_CODE = {
	17,				// High delay
	18,				// Low delay
	1,				// LSB format
	1,				// one start bit
	1,				// one stop bit
	{130, CA(1690)},	// start bit	
	{17,  CA(1000)},	// stop bit		
	{17,  CA(1240)},	// logical '1'	
	{17,  CA(400)},		// logical '0'
};

// static volatile unsigned int Microseconds;

static void DELAY_US(volatile unsigned int us) {
	while (us --);
}

/*
#define DELAY_US(US) \
	Microseconds = US; \
	while (Microseconds --) { \
		_NOP();_NOP();_NOP();_NOP(); _NOP();\
	}
*/

#define IR_CLKS(clks) \
	for(clocks = 0; clocks < clks; clocks ++) { \
		_CLR_IO(IRTX_PORT, IRTX_PIN); \
		DELAY_US(IR_CODE.carrierClockLowDelayUs);	\
		_SET_IO(IRTX_PORT, IRTX_PIN); \
		DELAY_US(IR_CODE.carrierClockHighDelayUs); \
	}

#define START_BIT() \
	IR_CLKS(IR_CODE.startBit.forestClocks); \
	DELAY_US(IR_CODE.startBit.flatTime)
	
#define STOP_BIT() \
	IR_CLKS(IR_CODE.stopBit.forestClocks); \
	DELAY_US(IR_CODE.stopBit.flatTime)
	
#define BIT(bit) \
	if(bit) { \
		IR_CLKS(IR_CODE.logicalBit1.forestClocks); \
		DELAY_US(IR_CODE.logicalBit1.flatTime); \
	} else { \
		IR_CLKS(IR_CODE.logicalBit0.forestClocks); \
		DELAY_US(IR_CODE.logicalBit0.flatTime); \
	}
		
void IRWriteData(const char *buffer, int numberOfBits) {
	int clocks, i;
	int bits, index;
	unsigned char mask;
	
	// LSB format
	index = 0;
	while (numberOfBits) {
		if(numberOfBits < 8) {
			bits = numberOfBits;
		} else {
			bits = 8;
		}
		numberOfBits -= bits;
		if(IR_CODE.isLSB) {
			mask = 1;
		} else {
			mask = 0x80;
		}
		for(i = 0; i < bits; i ++) {
			BIT(buffer[index] & mask);
			if(IR_CODE.isLSB) {
				mask <<= 1;
			} else {
				mask >>= 1;
			}
		}
		index ++;
	}
}

void IRWrite(const char *buffer, int numberOfBits) {
	int clocks;
	
	if (IR_CODE.sendStartBit)
		START_BIT();
	IRWriteData(buffer, numberOfBits);
	if (IR_CODE.sendStopBit)
		STOP_BIT();
}

int IRConfig(const char *buffer, int len) {
	if (len != sizeof(ir_code_t))
		return -1;
	memcpy((char *)&IR_CODE, buffer, sizeof(ir_code_t));
	
	IR_CODE.startBit.flatTime		= CA(IR_CODE.startBit.flatTime);
	IR_CODE.stopBit.flatTime		= CA(IR_CODE.stopBit.flatTime);
	IR_CODE.logicalBit0.flatTime	= CA(IR_CODE.logicalBit0.flatTime);
	IR_CODE.logicalBit1.flatTime	= CA(IR_CODE.logicalBit1.flatTime);
	return 0;
}
	
void IRMain() {
	const char ACCode[18] = {0x23,0xcb,0x26,0x01,0x00,0x20,0x58,0x0a,0x06,0x50,0x54,0x00,0x00,0x80,0x10,0x00,0x00,0xd1};
	
#ifdef TV_TEST
	const char TVCode[2] = {0x81, 0x44};
	
	IR_CODE.isLSB = 0;
	IR_CODE.sendStartBit = 0;
	IR_CODE.logicalBit0.forestClocks = 10;
	IR_CODE.logicalBit0.flatTime = 300;
	IR_CODE.logicalBit1.forestClocks = 10;
	IR_CODE.logicalBit0.flatTime = 800;
#endif
	while (1) {
		IRWrite(ACCode, 18 * 8);
		_delay_ms(1000);
	}
}
